// Object3D 扩展
// 叫 object3d，不叫 object 是因为代码里有很多变量叫 object，容易混淆
// 不叫 object3D 是因为小写的和其他模块更协调，也更好看一点
import * as THREE from 'three'

// 空物体，找不到某对象时用它代替，只是用来不报 undefined 的错
const EMPTY_OBJECT = new THREE.Object3D()
EMPTY_OBJECT.name == 'EMPTY'

// 判断物体及所有上级物体都可见
function isAllAncestorVisible(node: THREE.Object3D) {
  while (node.parent) {
    if (node.parent.visible == false) return false
    node = node.parent
  }
  return true
}

// 克隆对象和其材质
function cloneObjectWithMaterial(target: THREE.Object3D) {
  const cloned = target.clone()
  cloned.traverse((node) => {
    if (node instanceof THREE.Mesh) {
      const material = node.material as THREE.Material
      node.material = material.clone()
    }
  })
  return cloned
}

// 根据物体名称查找物体
function findObjectByName(container: THREE.Object3D, targetName: string): THREE.Object3D | undefined {
  let foundObject: THREE.Object3D | undefined = undefined
  container.traverse((node) => {
    if (node.name == targetName && foundObject == undefined) foundObject = node
  })
  return foundObject
}

// 根据物体名称查找网格
function findMeshByName(container: THREE.Object3D, targetName: string): THREE.Mesh | undefined {
  let foundObject: THREE.Mesh | undefined = undefined
  container.traverse((_node) => {
    const node = _node as THREE.Mesh
    if (node.isMesh && node.name == targetName && foundObject == undefined) foundObject = node
  })
  return foundObject
}

// 根据自定义条件查找物体
function findObjectBy(container: THREE.Object3D, test: (node: THREE.Object3D) => boolean): THREE.Object3D | undefined {
  let foundObject: THREE.Object3D | undefined = undefined
  container.traverse((node) => {
    if (test(node) && foundObject == undefined) foundObject = node
  })
  return foundObject
}

// 根据自定义条件查找网格
function findMeshBy(container: THREE.Object3D, test: (node: THREE.Object3D) => boolean): THREE.Mesh | undefined {
  let foundObject: THREE.Mesh | undefined = undefined
  container.traverse((_node) => {
    const node = _node as THREE.Mesh
    if (node.isMesh && test(node) && foundObject == undefined) foundObject = node as THREE.Mesh
  })
  return foundObject
}

// 清理对象（不管用，只有 renderer.dispose() + forceContextLoss() 管用）
function disposeObject(object: THREE.Object3D) {
  // 网格体
  if (object instanceof THREE.Mesh) {
    // 材质
    const material = object.material
    // 纹理
    Object.keys(material).forEach((key) => {
      const texture = material[key]
      if (texture instanceof THREE.Texture) {
        texture.dispose() // 贴图 dispose
        material[key] = undefined
      }
    })
    material.dispose?.() // 材质 dispose
    object.material = undefined
    // 几何体
    const geometry = object.geometry
    geometry.dispose() // 几何体 dispose
    object.geometry = undefined
  }
  // 递归
  object.children.forEach(disposeObject)
  // 解除树（不能要，会打乱树结构，进而使树的清理不完整）
  // if (object.parent) object.removeFromParent()
}

// 遍历 Object3D 树节点（区别是遍历某节点时会有条件判断）
function traverseWhen(
  node: THREE.Object3D,
  iterator: (node: THREE.Object3D) => void,
  tester: (node: THREE.Object3D) => boolean,
) {
  if (tester(node)) {
    iterator(node)
    node.children.forEach((childNode) => {
      traverseWhen(childNode, iterator, tester)
    })
  }
}

// 遍历 Object3D 树节点（不包含当前节点）（区别是遍历某节点时会有条件判断）
function traverseChildrenWhen(
  node: THREE.Object3D,
  iterator: (node: THREE.Object3D) => void,
  tester: (node: THREE.Object3D) => boolean,
) {
  node.children.forEach((childNode) => {
    traverseWhen(childNode, iterator, tester)
  })
}

// 遍历所有父节点
function traverseAncestorsWhen(
  node: THREE.Object3D,
  iterator: (node: THREE.Object3D) => void,
  tester: (node: THREE.Object3D) => boolean,
) {
  while (node.parent && tester(node.parent)) {
    iterator(node.parent)
    node = node.parent
  }
}

// 导出
export {
  EMPTY_OBJECT,
  isAllAncestorVisible,
  cloneObjectWithMaterial,
  findObjectByName,
  findMeshByName,
  findObjectBy,
  findMeshBy,
  disposeObject,
  traverseWhen,
  traverseChildrenWhen,
  traverseAncestorsWhen,
}
